/***********************************************************************************************************************
*                                                                                                                      *
* SPLASH build system v0.1                                                                                             *
*                                                                                                                      *
* Copyright (c) 2013 Andrew D. Zonenberg                                                                               *
* All rights reserved.                                                                                                 *
*                                                                                                                      *
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the     *
* following conditions are met:                                                                                        *
*                                                                                                                      *
*    * Redistributions of source code must retain the above copyright notice, this list of conditions, and the         *
*      following disclaimer.                                                                                           *
*                                                                                                                      *
*    * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the       *
*      following disclaimer in the documentation and/or other materials provided with the distribution.                *
*                                                                                                                      *
*    * Neither the name of the author nor the names of any contributors may be used to endorse or promote products     *
*      derived from this software without specific prior written permission.                                           *
*                                                                                                                      *
* THIS SOFTWARE IS PROVIDED BY THE AUTHORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED   *
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL *
* THE AUTHORS BE HELD LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES        *
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR       *
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT *
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE       *
* POSSIBILITY OF SUCH DAMAGE.                                                                                          *
*                                                                                                                      *
***********************************************************************************************************************/

#include "splashcore.h"
#include <inttypes.h>

using namespace std;

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Construction / destruction

NocgenLA::NocgenLA(
		const vector<string>& scriptlines,
		unsigned int& i,
		Nocgenv2GeneratedSourceNode* node,
		string fname)
		: NocgenNode("", "RedTinNocWrapper", true, true)
{
	string capture_clk = "clk_noc";
	
	uint32_t depth = 512;
	
	char tmp[256];
	char tmp2[256];
	for(; i<scriptlines.size(); i++)
	{
		string line = scriptlines[i];
		
		//Look up the opcode
		sscanf(line.c_str(), "%127s", tmp);
		string sopcode(tmp);

		//The node name
		if(sopcode == "la")
		{
			if(1 != sscanf(line.c_str(), "la %127s", tmp ))
				FatalError("Bad la line in node block of nocgen file \"%s\"\n", line.c_str(), fname.c_str());
			m_name = tmp;
		}
		
		//A signal
		else if(sopcode == "signal")
		{
			int nmatch = sscanf(line.c_str(), "signal %127s constants \"%255[^\"]\"", tmp, tmp2 );
			
			if( (nmatch != 1) && (nmatch != 2) )
				FatalError("Bad signal line in node block of nocgen file \"%s\"\n", line.c_str(), fname.c_str());
			m_signals.push_back(tmp);
			
			if(nmatch == 2)
				m_constants[tmp] = tmp2;
		}
		
		//The clock
		else if(sopcode == "clock")
		{
			if(1 != sscanf(line.c_str(), "clock %127s", tmp ))
				FatalError("Bad clock line in node block of nocgen file \"%s\"\n", line.c_str(), fname.c_str());
			capture_clk = tmp;
		}
		
		//Sample depth
		else if(sopcode == "depth")
		{
			if(1 != sscanf(line.c_str(), "depth %u", &depth ))
				FatalError("Bad depth line in node block of nocgen file \"%s\"\n", line.c_str(), fname.c_str());
		}

		//Done
		else if(sopcode == "endla")
			break;
		
		//Garbage
		else
			FatalError("Unrecognized opcode \"%s\" in la block of nocgen file \"%s\"\n",
			sopcode.c_str(), fname.c_str());
	}
	
	//Generate the list of signals
	uint32_t width = 0;
	string din = "{";
	for(auto x : m_signals)
	{
		width += node->GetSignalWidth(x);
		
		//Add comma if not first signal
		if(din.length() > 1)
			din += ",";
		din += string("\n\t\t\t") + x;
	}
	
	//Round signal width up to next multiple of 64
	uint32_t capture_width = width;
	if(capture_width % 64)
		capture_width += 64 - (capture_width % 64);
		
	//If LA has no signals at all, set it to 64 to avoid synthesis errors
	if(capture_width == 0)
		capture_width = 64;
	
	if(width >= 1024)
		FatalError("Too many signals for this LA (%u channels, max is 1024)\n", capture_width);
	else
	{
		int padding_width = capture_width - width;
		if(padding_width != 0)		//dont add padding if we fill LA exactly
		{
			if(width != 0)			//dont add comma  if we have zero signals
				din += ",";
			snprintf(tmp, sizeof(tmp), "\n\t\t\t%d'h0", padding_width);
			din += tmp;
		}
	}
	din += "\n\t\t}";
	
	vector<uint8_t> rom_contents;
	
	//Capture clock period (32 bits big-endian, in picoseconds)
	if(!node->IsClockNet(capture_clk))
		FatalError("Not a valid clock net (in LA block)\n");
	NocgenClockNet capclk = node->GetClockNet(capture_clk);
	uint32_t period = 1.0e12f / capclk.m_freq;		//in picoseconds
	rom_contents.push_back(period >> 24);
	rom_contents.push_back( (period >> 16) & 0xff);
	rom_contents.push_back( (period >> 8) & 0xff);
	rom_contents.push_back(period & 0xff);
	
	//Capture width (32 bits big-endian, in words)
	rom_contents.push_back(capture_width >> 24);
	rom_contents.push_back( (capture_width >> 16) & 0xff);
	rom_contents.push_back( (capture_width >> 8) & 0xff);
	rom_contents.push_back(capture_width & 0xff);
	
	//Capture depth (32 bits big-endian, in words)
	rom_contents.push_back(depth >> 24);
	rom_contents.push_back( (depth >> 16) & 0xff);
	rom_contents.push_back( (depth >> 8) & 0xff);
	rom_contents.push_back(depth & 0xff);
	
	for(auto x : m_signals)
	{
		//Signal width (8 bits)
		rom_contents.push_back(node->GetSignalWidth(x));
		
		//Signal name length (8 bits)
		rom_contents.push_back(x.length());
		
		//Signal name data
		for(unsigned int i=0; i<x.length(); i++)
			rom_contents.push_back(x[i]);
		
		//Symbol table filename replacement
		string symtab = m_constants[x];
		if(symtab != "")
			symtab = str_replace("$src", node->GetGraph()->GetSourceDir(), symtab);
		
		//Symbol table filename length (8 bits)
		rom_contents.push_back(symtab.length());
		
		//Symbol table name data 
		for(unsigned int i=0; i<symtab.length(); i++)
			rom_contents.push_back(symtab[i]);
	}
	
	//Complain if it's too big
	if(rom_contents.size() > 2048)
	{
		FatalError("Too many signals, or names too long, in logic analyzer of file \"%s\" (metadata ROM exceeds 2KB)\n", 
			fname.c_str());
	}
	
	//Hex-ify it
	while(rom_contents.size() < 2048)
		rom_contents.push_back(0);
	string rom = "";
	for(unsigned int i=0; i<2048; i+=4)
	{
		snprintf(tmp, sizeof(tmp), "%02x%02x%02x%02x\n",
			rom_contents[i] & 0xff, 
			rom_contents[i + 1] & 0xff, 
			rom_contents[i + 2] & 0xff, 
			rom_contents[i + 3] & 0xff);
		rom += tmp;
	}
	
	//Write the ROM
	string rfname = GetRomFileName(node);
	FILE* fp = fopen(rfname.c_str(), "w");
	if(!fp)
		FatalError("Couldn't open ROM file \"%s\" for writing\n", rfname.c_str());
	fputs(rom.c_str(), fp);
	fclose(fp);
	m_params["ROMFILE"] = string("\"") + rfname + "\"";
	
	//Set capture depth	and width
	snprintf(tmp, sizeof(tmp), "%d", depth);
	m_params["DEPTH"] = tmp;
	snprintf(tmp, sizeof(tmp), "%u", capture_width);
	m_params["WIDTH"] = tmp;
	
	//Hook up the capture signals
	m_ports["din"] = din;
	m_ports["capture_clk"] = capture_clk;
}

string NocgenLA::GetRomFileName(Nocgenv2GeneratedSourceNode* node)
{
	string fname = node->GetOutputPath();
	return str_replace(".v", string("_la_") + m_name + "_mem.txt", fname);
}
